package jp.tkym.labs.gwt.gantt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

/**
 * <table>
 * 	<tr><td>[0,0]</td><td>[0,1]</td><td>[0,2]</td><td>[0,3]</td></tr>
 * 	<tr><td>[1,0]</td><td colspan=2>[1,1]</td></tr>
 * </table>
 * @author takayama
 *
 */
public class FlexTableBindModel{
	private List<List<FlexTableBindSource>> sources = new ArrayList<List<FlexTableBindSource>>();
	private Map<Integer, Set<Integer>> invalidIndex = new HashMap<Integer, Set<Integer>>();
	
	public int rowSize(){
		return sources.size();
	}
	
	public void clear(){
		sources.clear();
	}
	
	public int colSize(){
		int maxColumn = 0;
		for(int row=0; row<sources.size(); row++){
			int columnSize = calculateColumnSize(row);
			if(columnSize > maxColumn) maxColumn = columnSize;
		}
		return maxColumn;
	}
	
	public int calculateColumnSize(int row){
		List<FlexTableBindSource> column = sources.get(row);
		int ret = 0;
		for(int col=0; col<column.size(); col++){
			if(isInvalid(row, col)){
				ret += column.get(col).getColspan();
			}
		}
		return ret;
	}
	
	public FlexTableBindCell cell(int row, int col){
		return new FlexTableBindCell(row, col, this);
	}
	
	public void allocate(int row, int col){
		allocateByRowIndex(row);
		allocateByColIndex(col, sources.get(row));
	}
	
	void allocateByRowIndex(int row){
		int needSize = row + 1;
		int shortage = shortage(sources.size(), needSize);
		for(int i=0; i<shortage; i++){
			sources.add(new ArrayList<FlexTableBindSource>());
		}
	}
	
	List<List<FlexTableBindSource>> sources(){
		return sources;
	}
	
	int shortage(int now, int need){
		if(now >= need) return 0;
		if(need < 1) throw new IllegalArgumentException();
		return need - now; 
	}
	
	void allocateByColIndex(int col, List<FlexTableBindSource> list){
		int needSize = col + 1;
		int shortage = shortage(list.size(), needSize);
		for(int i=0; i<shortage; i++){
			list.add(new FlexTableBindSource(null));
		}
	}
	
	void setBindSource(int row, int col, FlexTableBindSource source){
		allocate(row, col);
		sources.get(row).set(col, source);
	}

	FlexTableBindSource getBindSource(int row, int col){
		return sources.get(row).get(col);
	}
	
	void fill(){
		int maxColumn = colSize();
		for(int row=0; row<sources.size(); row++)
			allocate(row, maxColumn-1);
	}
	
	void invalidIndex(int row, int col){
		Set<Integer> cols = invalidIndex.get(row);
		if(cols == null) {
			cols = new HashSet<Integer>();
			invalidIndex.put(row, cols);
		}
		cols.add(col);
	}
	
	boolean isInvalid(int row, int col){
		Set<Integer> cols = invalidIndex.get(row);
		if(cols == null) return false;
		return cols.contains(col);
	}
	
	void ensureSpan(){
		invalidIndex.clear();
		for(int row=0; row<sources.size(); row++){
			List<FlexTableBindSource> columns = sources.get(row);
			for(int col=0; col<columns.size(); col++){
				setInvalidBySpan(row, col, columns.get(col));
			}
		}
	}
	
	void setInvalidBySpan(int row, int col, FlexTableBindSource source){
		if(source.getColspan() > 1){
			for(int i=1; i<source.getColspan(); i++)
				invalidIndex(row, col+i);
		} 
		if(source.getRowspan() > 1){
			for(int i=1; i<source.getRowspan(); i++)
				invalidIndex(row+i, col);
		} 
	}
	
	public class FlexTableBindCell{
		private FlexTableBindSource source;
		
		FlexTableBindCell(int row, int col, FlexTableBindModel model){
			model.allocate(row, col);
			source = model.getBindSource(row, col);
			if(source == null) 
				source = new FlexTableBindSource(null);
			model.setBindSource(row, col, source);
		}
		
		public FlexTableBindCell text(String text){
			source.setWidget(new Label(text));
			return this;
		}
		
		public FlexTableBindCell widget(Widget widget){
			source.setWidget(widget);
			return this;
		}
		
		public FlexTableBindCell colspan(int colspan){
			source.setColspan(colspan);
			return this;
		}
		
		public FlexTableBindCell rowspan(int rowspan){
			source.setRowspan(rowspan);
			return this;
		}
		
		public FlexTableBindCell styleName(String styleName){
			source.setStyleName(styleName);
			return this;
		}
	}
}